home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / inetutil.1 / inetutil / inetutils-1.1 / tftp / tftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-22  |  10.6 KB  |  459 lines

  1. /*
  2.  * Copyright (c) 1983, 1993
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char sccsid[] = "@(#)tftp.c    8.1 (Berkeley) 6/6/93";
  36. #endif /* not lint */
  37.  
  38. /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
  39.  
  40. /*
  41.  * TFTP User Program -- Protocol Machines
  42.  */
  43.  
  44. #ifdef HAVE_CONFIG_H
  45. #include <config.h>
  46. #endif
  47.  
  48. #include <sys/types.h>
  49. #include <sys/socket.h>
  50. #include <sys/time.h>
  51.  
  52. #include <netinet/in.h>
  53.  
  54. #include <arpa/tftp.h>
  55.  
  56. #include <errno.h>
  57. #include <setjmp.h>
  58. #include <signal.h>
  59. #include <stdio.h>
  60. #include <unistd.h>
  61.  
  62. #include "extern.h"
  63. #include "tftpsubs.h"
  64.  
  65. extern    int errno;
  66.  
  67. extern  struct sockaddr_in peeraddr;    /* filled in by main */
  68. extern  int     f;            /* the opened socket */
  69. extern  int     trace;
  70. extern  int     verbose;
  71. extern  int     rexmtval;
  72. extern  int     maxtimeout;
  73.  
  74. #define PKTSIZE    SEGSIZE+4
  75. char    ackbuf[PKTSIZE];
  76. int    timeout;
  77. jmp_buf    toplevel;
  78. jmp_buf    timeoutbuf;
  79.  
  80. static void nak __P((int));
  81. static int makerequest __P((int, const char *, struct tftphdr *, const char *));
  82. static void printstats __P((const char *, unsigned long));
  83. static void startclock __P((void));
  84. static void stopclock __P((void));
  85. static void timer __P((int));
  86. static void tpacket __P((const char *, struct tftphdr *, int));
  87.  
  88. /*
  89.  * Send the requested file.
  90.  */
  91. void
  92. sendfile(fd, name, mode)
  93.     int fd;
  94.     char *name;
  95.     char *mode;
  96. {
  97.     register struct tftphdr *ap;       /* data and ack packets */
  98.     struct tftphdr *r_init(), *dp;
  99.     register int n;
  100.     volatile int block, size, convert;
  101.     volatile unsigned long amount;
  102.     struct sockaddr_in from;
  103.     int fromlen;
  104.     FILE *file;
  105.  
  106.     startclock();        /* start stat's clock */
  107.     dp = r_init();        /* reset fillbuf/read-ahead code */
  108.     ap = (struct tftphdr *)ackbuf;
  109.     file = fdopen(fd, "r");
  110.     convert = !strcmp(mode, "netascii");
  111.     block = 0;
  112.     amount = 0;
  113.  
  114.     signal(SIGALRM, timer);
  115.     do {
  116.         if (block == 0)
  117.             size = makerequest(WRQ, name, dp, mode) - 4;
  118.         else {
  119.         /*    size = read(fd, dp->th_data, SEGSIZE);     */
  120.             size = readit(file, &dp, convert);
  121.             if (size < 0) {
  122.                 nak(errno + 100);
  123.                 break;
  124.             }
  125.             dp->th_opcode = htons((u_short)DATA);
  126.             dp->th_block = htons((u_short)block);
  127.         }
  128.         timeout = 0;
  129.         (void) setjmp(timeoutbuf);
  130. send_data:
  131.         if (trace)
  132.             tpacket("sent", dp, size + 4);
  133.         n = sendto(f, dp, size + 4, 0,
  134.             (struct sockaddr *)&peeraddr, sizeof(peeraddr));
  135.         if (n != size + 4) {
  136.             perror("tftp: sendto");
  137.             goto abort;
  138.         }
  139.         read_ahead(file, convert);
  140.         for ( ; ; ) {
  141.             alarm(rexmtval);
  142.             do {
  143.                 fromlen = sizeof(from);
  144.                 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
  145.                     (struct sockaddr *)&from, &fromlen);
  146.             } while (n <= 0);
  147.             alarm(0);
  148.             if (n < 0) {
  149.                 perror("tftp: recvfrom");
  150.                 goto abort;
  151.             }
  152.             peeraddr.sin_port = from.sin_port;    /* added */
  153.             if (trace)
  154.                 tpacket("received", ap, n);
  155.             /* should verify packet came from server */
  156.             ap->th_opcode = ntohs(ap->th_opcode);
  157.             ap->th_block = ntohs(ap->th_block);
  158.             if (ap->th_opcode == ERROR) {
  159.                 printf("Error code %d: %s\n", ap->th_code,
  160.                     ap->th_msg);
  161.                 goto abort;
  162.             }
  163.             if (ap->th_opcode == ACK) {
  164.                 int j;
  165.  
  166.                 if (ap->th_block == block) {
  167.                     break;
  168.                 }
  169.                 /* On an error, try to synchronize
  170.                  * both sides.
  171.                  */
  172.                 j = synchnet(f);
  173.                 if (j && trace) {
  174.                     printf("discarded %d packets\n",
  175.                             j);
  176.                 }
  177.                 if (ap->th_block == (block-1)) {
  178.                     goto send_data;
  179.                 }
  180.             }
  181.         }
  182.         if (block > 0)
  183.             amount += size;
  184.         block++;
  185.     } while (size == SEGSIZE || block == 1);
  186. abort:
  187.     fclose(file);
  188.     stopclock();
  189.     if (amount > 0)
  190.         printstats("Sent", amount);
  191. }
  192.  
  193. /*
  194.  * Receive a file.
  195.  */
  196. void
  197. recvfile(fd, name, mode)
  198.     int fd;
  199.     char *name;
  200.     char *mode;
  201. {
  202.     register struct tftphdr *ap;
  203.     struct tftphdr *dp, *w_init();
  204.     register int n;
  205.     volatile int block, size, firsttrip;
  206.     volatile unsigned long amount;
  207.     struct sockaddr_in from;
  208.     int fromlen;
  209.     FILE *file;
  210.     volatile int convert;        /* true if converting crlf -> lf */
  211.  
  212.     startclock();
  213.     dp = w_init();
  214.     ap = (struct tftphdr *)ackbuf;
  215.     file = fdopen(fd, "w");
  216.     convert = !strcmp(mode, "netascii");
  217.     block = 1;
  218.     firsttrip = 1;
  219.     amount = 0;
  220.  
  221.     signal(SIGALRM, timer);
  222.     do {
  223.         if (firsttrip) {
  224.             size = makerequest(RRQ, name, ap, mode);
  225.             firsttrip = 0;
  226.         } else {
  227.             ap->th_opcode = htons((u_short)ACK);
  228.             ap->th_block = htons((u_short)(block));
  229.             size = 4;
  230.             block++;
  231.         }
  232.         timeout = 0;
  233.         (void) setjmp(timeoutbuf);
  234. send_ack:
  235.         if (trace)
  236.             tpacket("sent", ap, size);
  237.         if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
  238.             sizeof(peeraddr)) != size) {
  239.             alarm(0);
  240.             perror("tftp: sendto");
  241.             goto abort;
  242.         }
  243.         write_behind(file, convert);
  244.         for ( ; ; ) {
  245.             alarm(rexmtval);
  246.             do  {
  247.                 fromlen = sizeof(from);
  248.                 n = recvfrom(f, dp, PKTSIZE, 0,
  249.                     (struct sockaddr *)&from, &fromlen);
  250.             } while (n <= 0);
  251.             alarm(0);
  252.             if (n < 0) {
  253.                 perror("tftp: recvfrom");
  254.                 goto abort;
  255.             }
  256.             peeraddr.sin_port = from.sin_port;    /* added */
  257.             if (trace)
  258.                 tpacket("received", dp, n);
  259.             /* should verify client address */
  260.             dp->th_opcode = ntohs(dp->th_opcode);
  261.             dp->th_block = ntohs(dp->th_block);
  262.             if (dp->th_opcode == ERROR) {
  263.                 printf("Error code %d: %s\n", dp->th_code,
  264.                     dp->th_msg);
  265.                 goto abort;
  266.             }
  267.             if (dp->th_opcode == DATA) {
  268.                 int j;
  269.  
  270.                 if (dp->th_block == block) {
  271.                     break;        /* have next packet */
  272.                 }
  273.                 /* On an error, try to synchronize
  274.                  * both sides.
  275.                  */
  276.                 j = synchnet(f);
  277.                 if (j && trace) {
  278.                     printf("discarded %d packets\n", j);
  279.                 }
  280.                 if (dp->th_block == (block-1)) {
  281.                     goto send_ack;    /* resend ack */
  282.                 }
  283.             }
  284.         }
  285.     /*    size = write(fd, dp->th_data, n - 4); */
  286.         size = writeit(file, &dp, n - 4, convert);
  287.         if (size < 0) {
  288.             nak(errno + 100);
  289.             break;
  290.         }
  291.         amount += size;
  292.     } while (size == SEGSIZE);
  293. abort:                        /* ok to ack, since user */
  294.     ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
  295.     ap->th_block = htons((u_short)block);
  296.     (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
  297.         sizeof(peeraddr));
  298.     write_behind(file, convert);        /* flush last buffer */
  299.     fclose(file);
  300.     stopclock();
  301.     if (amount > 0)
  302.         printstats("Received", amount);
  303. }
  304.  
  305. static int
  306. makerequest(request, name, tp, mode)
  307.     int request;
  308.     const char *name;
  309.     struct tftphdr *tp;
  310.     const char *mode;
  311. {
  312.     register char *cp;
  313.  
  314.     tp->th_opcode = htons((u_short)request);
  315.     cp = tp->th_stuff;
  316.     strcpy(cp, name);
  317.     cp += strlen(name);
  318.     *cp++ = '\0';
  319.     strcpy(cp, mode);
  320.     cp += strlen(mode);
  321.     *cp++ = '\0';
  322.     return (cp - (char *)tp);
  323. }
  324.  
  325. struct errmsg {
  326.     int    e_code;
  327.     char    *e_msg;
  328. } errmsgs[] = {
  329.     { EUNDEF,    "Undefined error code" },
  330.     { ENOTFOUND,    "File not found" },
  331.     { EACCESS,    "Access violation" },
  332.     { ENOSPACE,    "Disk full or allocation exceeded" },
  333.     { EBADOP,    "Illegal TFTP operation" },
  334.     { EBADID,    "Unknown transfer ID" },
  335.     { EEXISTS,    "File already exists" },
  336.     { ENOUSER,    "No such user" },
  337.     { -1,        0 }
  338. };
  339.  
  340. /*
  341.  * Send a nak packet (error message).
  342.  * Error code passed in is one of the
  343.  * standard TFTP codes, or a UNIX errno
  344.  * offset by 100.
  345.  */
  346. static void
  347. nak(error)
  348.     int error;
  349. {
  350.     register struct errmsg *pe;
  351.     register struct tftphdr *tp;
  352.     int length;
  353.     char *strerror();
  354.  
  355.     tp = (struct tftphdr *)ackbuf;
  356.     tp->th_opcode = htons((u_short)ERROR);
  357.     tp->th_code = htons((u_short)error);
  358.     for (pe = errmsgs; pe->e_code >= 0; pe++)
  359.         if (pe->e_code == error)
  360.             break;
  361.     if (pe->e_code < 0) {
  362.         pe->e_msg = strerror(error - 100);
  363.         tp->th_code = EUNDEF;
  364.     }
  365.     strcpy(tp->th_msg, pe->e_msg);
  366.     length = strlen(pe->e_msg) + 4;
  367.     if (trace)
  368.         tpacket("sent", tp, length);
  369.     if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
  370.         sizeof(peeraddr)) != length)
  371.         perror("nak");
  372. }
  373.  
  374. static void
  375. tpacket(s, tp, n)
  376.     const char *s;
  377.     struct tftphdr *tp;
  378.     int n;
  379. {
  380.     static char *opcodes[] =
  381.        { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
  382.     register char *cp, *file;
  383.     u_short op = ntohs(tp->th_opcode);
  384.     char *index();
  385.  
  386.     if (op < RRQ || op > ERROR)
  387.         printf("%s opcode=%x ", s, op);
  388.     else
  389.         printf("%s %s ", s, opcodes[op]);
  390.     switch (op) {
  391.  
  392.     case RRQ:
  393.     case WRQ:
  394.         n -= 2;
  395.         file = cp = tp->th_stuff;
  396.         cp = index(cp, '\0');
  397.         printf("<file=%s, mode=%s>\n", file, cp + 1);
  398.         break;
  399.  
  400.     case DATA:
  401.         printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
  402.         break;
  403.  
  404.     case ACK:
  405.         printf("<block=%d>\n", ntohs(tp->th_block));
  406.         break;
  407.  
  408.     case ERROR:
  409.         printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
  410.         break;
  411.     }
  412. }
  413.  
  414. struct timeval tstart;
  415. struct timeval tstop;
  416.  
  417. static void
  418. startclock()
  419. {
  420.  
  421.     (void)gettimeofday(&tstart, NULL);
  422. }
  423.  
  424. static void
  425. stopclock()
  426. {
  427.  
  428.     (void)gettimeofday(&tstop, NULL);
  429. }
  430.  
  431. static void
  432. printstats(direction, amount)
  433.     const char *direction;
  434.     unsigned long amount;
  435. {
  436.     double delta;
  437.             /* compute delta in 1/10's second units */
  438.     delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
  439.         ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
  440.     delta = delta/10.;      /* back to seconds */
  441.     printf("%s %d bytes in %.1f seconds", direction, amount, delta);
  442.     if (verbose)
  443.         printf(" [%.0f bits/sec]", (amount*8.)/delta);
  444.     putchar('\n');
  445. }
  446.  
  447. static void
  448. timer(sig)
  449.     int sig;
  450. {
  451.  
  452.     timeout += rexmtval;
  453.     if (timeout >= maxtimeout) {
  454.         printf("Transfer timed out.\n");
  455.         longjmp(toplevel, -1);
  456.     }
  457.     longjmp(timeoutbuf, 1);
  458. }
  459.